﻿using System;
using System.Collections.Generic;
using System.Text;

namespace System
{
	/// <summary>
	/// Defines Extension-Methods for classes in the <see cref="System"/> namespace.
	/// </summary>
	public static class ArgusLibExtensions
	{
		#region Enum
		/// <summary>
		/// Checks whether a flag value is set.
		/// </summary>
		public static bool HasFlag(this Enum value, Enum flag)
		{
			if (value.GetType() != flag.GetType())
				throw new ArgumentException("Both arguments must be of the same type.", "value, flag");

			UInt64 v = (UInt64)Convert.ToUInt64(value);
			UInt64 f = (UInt64)Convert.ToUInt64(flag);
			return (v & f) == f;
		}

		/// <summary>
		/// Converts an <see cref="Enum"/> to an <see cref="UInt64"/>.
		/// </summary>
		public static UInt64 ToUInt64(this Enum e)
		{
			return (UInt64)Convert.ToUInt64(e);
		}

		public static Int64 ToInt64(this Enum e)
		{
			return (Int64)Convert.ToInt64(e);
		}
		#endregion

		#region Array
		public static void SwapOrder<T>(this T[] array, int count)
		{
			T[] buffer = new T[array.Length];
			array.CopyTo(buffer, 0);
			for (long i = 0; i < count; i++)
			{
				array[count - (i + 1)] = buffer[i];
			}
		}

		public static void SwapOrder<T>(this T[] array)
		{
			array.SwapOrder(array.Length);
		}
		#endregion

		#region Object
		public static string ToFormatString(this object value, IFormatProvider formatProvider, string format)
		{
			return String.Format(formatProvider, "{0:" + format + "}", value);
		}
		#endregion

		#region String
		public static string[] Group(this string s, string start, string end)
		{
			List<string> RetVal = new List<string>();
			int a = s.IndexOf(start)+1;
			int b = s.IndexOf(end, a);
			while (a > 0 && b > -1)
			{
				if (b - a < 1)
					continue;

				RetVal.Add(s.Substring(a, b - a));

				a = s.IndexOf(start, b+1)+1;
				b = s.IndexOf(end, a);
			}
			return RetVal.ToArray();
		}

		public static string[] Group(this string s, char start, char end)
		{
			List<string> RetVal = new List<string>();
			int a = s.IndexOf(start) + 1;
			int b = s.IndexOf(end, a);
			while (a > -1 && b > -1)
			{
				if (b - a < 1)
					continue;

				RetVal.Add(s.Substring(a, b - a));

				a = s.IndexOf(start, b + 1) + 1;
				b = s.IndexOf(end, a);
			}
			return RetVal.ToArray();
		}

		public static string[] Group(this string s, char[] start, char[] end)
		{
			List<string> RetVal = new List<string>();
			int a = s.IndexOfAny(start) + 1;
			int b = s.IndexOfAny(end, a);
			while (a > -1 && b > -1)
			{
				if (b - a < 1)
					continue;

				RetVal.Add(s.Substring(a, b - a));

				a = s.IndexOfAny(start, b + 1) + 1;
				b = s.IndexOfAny(end, a);
			}
			return RetVal.ToArray();
		}

		public static bool Fits(this string s, string pattern)
		{
			return s.Fits(pattern, false);
		}

		/// <summary>
		/// Tests if a <see cref="string"/> fits a given pattern. The pattern can be any string and can contain '*' and '?' as placeholders.
		/// </summary>
		/// <param name="s"></param>
		/// <param name="pattern"></param>
		/// <returns></returns>
		public static bool Fits(this string s, string pattern, bool ignoreCase)
		{
			if (pattern == null)
				throw new ArgumentNullException("pattern");

			if (ignoreCase == true)
			{
				s = s.ToLowerInvariant();
				pattern = pattern.ToLowerInvariant();
			}

			string[] splitAsteriks = pattern.Split('*');

			int index = 0;
			{
				string[] splitQuestion = splitAsteriks[0].Split('?');
				if (s.StartsWith(splitQuestion[0]) == false)
					return false;
				if (s.Length < splitAsteriks[0].Length)
					return false;

				index = splitQuestion[0].Length;
				for (int i = 1; i < splitQuestion.Length; i++)
				{
					index++;
					if (splitQuestion[i].Length < 1)
						continue;
					if (s.Substring(index, splitQuestion[i].Length) != splitQuestion[i])
						return false;
					index += splitQuestion[i].Length;
				}
			}

			for (int i = 1; i < splitAsteriks.Length - 1; i++)
			{
				string[] splitQuestion = splitAsteriks[i].Split('?');
				index = s.IndexOf(splitQuestion[0], index);
				if (index < 0)
					return false;
				if (s.Length - index < splitAsteriks[i].Length)
					return false;
				index += splitQuestion[0].Length;
				for (int j = 1; j < splitQuestion.Length; j++)
				{
					index++;
					if (splitQuestion[j].Length < 1)
						continue;
					if (s.Substring(index, splitQuestion[j].Length) != splitQuestion[j])
						return false;
					index += splitQuestion[j].Length;
				}
			}

			{
				string[] splitQuestion = splitAsteriks[splitAsteriks.Length-1].Split('?');
				if (s.EndsWith(splitQuestion[splitQuestion.Length-1]) == false)
					return false;
				if (s.Length - index < splitAsteriks[splitAsteriks.Length - 1].Length)
					return false;

				index = s.Length - splitAsteriks[splitAsteriks.Length-1].Length;
				for (int i = 0; i < splitQuestion.Length-1; i++)
				{
					index++;
					if (splitQuestion[i].Length < 1)
						continue;
					if (s.Substring(index, splitQuestion[i].Length) != splitQuestion[i])
						return false;
					index += splitQuestion[i].Length;
				}
			}

			return true;
		}

		public static bool FitsAny(this string s, IEnumerable<string> patterns)
		{
			return s.FitsAny(patterns, false);
		}

		public static bool FitsAny(this string s, IEnumerable<string> patterns, bool ignoreCase)
		{
			foreach (string pattern in patterns)
			{
				if (s.Fits(pattern, ignoreCase) == true)
					return true;
			}
			return false;
		}

		public static string RemoveChars(this string s, IEnumerable<char> chars)
		{
			StringBuilder sb = new StringBuilder(s.Length);
			for (int i = 0; i < s.Length; i++)
			{
				if (chars.Contains(s[i]) == false)
					sb.Append(s[i]);
			}
			return sb.ToString();
		}

		public static bool ContainsAny(this string s, IEnumerable<char> chars)
		{
			foreach (char c in chars)
			{
				if (s.Contains(c) == true)
					return true;
			}
			return false;
		}
		#endregion

		#region Type
		public static bool Is(this Type thisType, Type type)
		{
			if (thisType == type || thisType.IsSubclassOf(type) == true)
				return true;

			if (type.IsInterface)
			{
				if (thisType.GetInterface(type.FullName) != null)
					return true;
			}

			return false;
		}
		#endregion

		#region IEnumerable<T>
		public static bool Contains<T>(this IEnumerable<T> e, T item)
		{
			foreach (T t in e)
			{
				if (t.Equals(item) == true)
					return true;
			}

			return false;
		}
		#endregion
	}
}
